/*
 * ciscyfilter.{cc,hh} -- CISCy type filter
 * Eddie Kohler
 *
 * Copyright (c) 2007 Regents of the University of California
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "crapfilter2.hh"
#include <click/glue.hh>
#include <click/error.hh>
#include <click/confparse.hh>
#include <click/straccum.hh>
#include <clicknet/ip.h>
#include <clicknet/udp.h>
#include <click/hashmap.hh>
#include <click/integers.hh>
#include <click/nameinfo.hh>
CLICK_DECLS

CiscyFilter::CiscyFilter()
{
}

CiscyFilter::~CiscyFilter()
{
}

#define CISCY_UNROLL 2
#define CISCY_EQ2 1

int
CiscyFilter::initialize(ErrorHandler *)
{
    Insn in;

    // IP nonfrag
    in.cmd = D_MEQ | (1 << NARGSHIFT) | 4;
    in.arg[0] = 0;
    in.arg[1] = ntohl(0x00001FFF);
    _insn.push_back(in);

    // TCP or UDP
    in.cmd = D_MEQ | (3 << NARGSHIFT) | 8;
    in.arg[0] = ntohl(0x00060000);
    in.arg[1] = ntohl(0x00ff0000);
    in.arg[2] = ntohl(0x00110000);
    in.arg[3] = ntohl(0x00ff0000);
    _insn.push_back(in);

#if CISCY_EQ2
    // TCP source or dst port
    in.cmd = L1_SEQ2 | (2 << NARGSHIFT) | 0 | NEG;
    in.sarg[0] = ntohs(0x0051);
    in.sarg[1] = ntohs(0x001e);
    in.sarg[2] = ntohs(0x0032);
    _insn.push_back(in);

#else
    // TCP source port
    in.cmd = L1_SEQ | (2 << NARGSHIFT) | 0 | NEG;
    in.sarg[0] = ntohs(0x0051);
    in.sarg[1] = ntohs(0x001e);
    in.sarg[2] = ntohs(0x0032);
    _insn.push_back(in);

    // TCP dst port
    in.cmd = L1_SEQ | (2 << NARGSHIFT) | 2 | NEG;
    in.sarg[0] = ntohs(0x0051);
    in.sarg[1] = ntohs(0x001e);
    in.sarg[2] = ntohs(0x0032);
    _insn.push_back(in);
#endif

#if CISCY_EQ2
    // source host
    in.cmd = D_EQ2 | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x01000002);
    in.arg[1] = ntohl(0x121a0409);
    in.arg[2] = ntohl(0x09040301);
    in.arg[3] = ntohl(0x05040108);
    in.arg[4] = ntohl(0x09010102);
    in.arg[5] = ntohl(0x3001032b);
    in.arg[6] = ntohl(0x0a04022b);
    _insn.push_back(in);
    
    in.cmd = D_EQ2 | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x2b5e0103);
    in.arg[1] = ntohl(0x66310204);
    in.arg[2] = ntohl(0xc931275c);
    in.arg[3] = ntohl(0x28282828);
    in.arg[4] = ntohl(0x29292929);
    in.arg[5] = ntohl(0x2a2a2a2a);
    in.arg[6] = ntohl(0x2b2b2b2b);
    _insn.push_back(in);
    
    in.cmd = D_EQ2 | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x2c2c2c2c);
    in.arg[1] = ntohl(0x2d2d2d2d);
    in.arg[2] = ntohl(0x2e2e2e2e);
    in.arg[3] = ntohl(0x2f2f2f2f);
    in.arg[4] = ntohl(0x30303030);
    in.arg[5] = ntohl(0x31313131);
    in.arg[6] = ntohl(0x32323232);
    _insn.push_back(in);
    
    in.cmd = D_EQ2 | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x33333333);
    in.arg[1] = ntohl(0x34343434);
    in.arg[2] = ntohl(0x35353535);
    in.arg[3] = ntohl(0x36363636);
    in.arg[4] = ntohl(0x37373737);
    in.arg[5] = ntohl(0x38383838);
    in.arg[6] = ntohl(0x394b4b4b);
    _insn.push_back(in);
    
    in.cmd = D_EQ2 | (2 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x4c4c4c4c);
    in.arg[1] = ntohl(0xb1b1b1b1);
    in.arg[2] = ntohl(0xc1c1c1c1);
    _insn.push_back(in);

#else
    // source host
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x01000002);
    in.arg[1] = ntohl(0x121a0409);
    in.arg[2] = ntohl(0x09040301);
    in.arg[3] = ntohl(0x05040108);
    in.arg[4] = ntohl(0x09010102);
    in.arg[5] = ntohl(0x3001032b);
    in.arg[6] = ntohl(0x0a04022b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x2b5e0103);
    in.arg[1] = ntohl(0x66310204);
    in.arg[2] = ntohl(0xc931275c);
    in.arg[3] = ntohl(0x28282828);
    in.arg[4] = ntohl(0x29292929);
    in.arg[5] = ntohl(0x2a2a2a2a);
    in.arg[6] = ntohl(0x2b2b2b2b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x2c2c2c2c);
    in.arg[1] = ntohl(0x2d2d2d2d);
    in.arg[2] = ntohl(0x2e2e2e2e);
    in.arg[3] = ntohl(0x2f2f2f2f);
    in.arg[4] = ntohl(0x30303030);
    in.arg[5] = ntohl(0x31313131);
    in.arg[6] = ntohl(0x32323232);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x33333333);
    in.arg[1] = ntohl(0x34343434);
    in.arg[2] = ntohl(0x35353535);
    in.arg[3] = ntohl(0x36363636);
    in.arg[4] = ntohl(0x37373737);
    in.arg[5] = ntohl(0x38383838);
    in.arg[6] = ntohl(0x394b4b4b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (2 << NARGSHIFT) | 0x12 | NEG;
    in.arg[0] = ntohl(0x4c4c4c4c);
    in.arg[1] = ntohl(0xb1b1b1b1);
    in.arg[2] = ntohl(0xc1c1c1c1);
    _insn.push_back(in);

    // destination host
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x16 | NEG;
    in.arg[0] = ntohl(0x01000002);
    in.arg[1] = ntohl(0x121a0409);
    in.arg[2] = ntohl(0x09040301);
    in.arg[3] = ntohl(0x05040108);
    in.arg[4] = ntohl(0x09010102);
    in.arg[5] = ntohl(0x3001032b);
    in.arg[6] = ntohl(0x0a04022b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x16 | NEG;
    in.arg[0] = ntohl(0x2b5e0103);
    in.arg[1] = ntohl(0x66310204);
    in.arg[2] = ntohl(0xc931275c);
    in.arg[3] = ntohl(0x28282828);
    in.arg[4] = ntohl(0x29292929);
    in.arg[5] = ntohl(0x2a2a2a2a);
    in.arg[6] = ntohl(0x2b2b2b2b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x16 | NEG;
    in.arg[0] = ntohl(0x2c2c2c2c);
    in.arg[1] = ntohl(0x2d2d2d2d);
    in.arg[2] = ntohl(0x2e2e2e2e);
    in.arg[3] = ntohl(0x2f2f2f2f);
    in.arg[4] = ntohl(0x30303030);
    in.arg[5] = ntohl(0x31313131);
    in.arg[6] = ntohl(0x32323232);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (6 << NARGSHIFT) | 0x16 | NEG;
    in.arg[0] = ntohl(0x33333333);
    in.arg[1] = ntohl(0x34343434);
    in.arg[2] = ntohl(0x35353535);
    in.arg[3] = ntohl(0x36363636);
    in.arg[4] = ntohl(0x37373737);
    in.arg[5] = ntohl(0x38383838);
    in.arg[6] = ntohl(0x394b4b4b);
    _insn.push_back(in);
    
    in.cmd = D_EQ | (2 << NARGSHIFT) | 0x16 | NEG;
    in.arg[0] = ntohl(0x4c4c4c4c);
    in.arg[1] = ntohl(0xb1b1b1b1);
    in.arg[2] = ntohl(0xc1c1c1c1);
    _insn.push_back(in);
#endif

    return 0;
}

void
CiscyFilter::push(int, Packet *p)
{
    for (int off = 0; off < _insn.size(); off++) {
	const Insn *in = _insn.begin() + off;
	int offset = in->cmd & OFFSETMASK;
	const uint32_t *dp = 0;
	if ((in->cmd >> 24) < (L1_SEQ >> 24))
	    dp = ((const uint32_t *) (p->network_header() + offset));
	else
	    dp = ((const uint32_t *) (p->transport_header() + offset));
	int n = (in->cmd >> NARGSHIFT) & NARGMASK;
	switch (in->cmd >> 24) {
	  case D_EQ >> 24: {
	      uint32_t data = *dp;
#if CISCY_UNROLL == 2
	      int i = 0;
	      switch (n % 4) {
		  do {
		    case 3:
		      if (data == in->arg[i++]) goto success;
		    case 2:
		      if (data == in->arg[i++]) goto success;
		    case 1:
		      if (data == in->arg[i++]) goto success;
		    case 0:
		      if (data == in->arg[i++]) goto success;
		  } while (i <= n);
	      }
#elif CISCY_UNROLL
	      int i = 0;
	      switch (n) {
		case 7: if (data == in->arg[i++]) goto success;
		case 6: if (data == in->arg[i++]) goto success;
		case 5: if (data == in->arg[i++]) goto success;
		case 4: if (data == in->arg[i++]) goto success;
		case 3: if (data == in->arg[i++]) goto success;
		case 2: if (data == in->arg[i++]) goto success;
		case 1: if (data == in->arg[i++]) goto success;
		case 0: if (data == in->arg[i++]) goto success;
	      }
#else
	      for (int i = 0; i <= n; i++)
		  if (data == in->arg[i])
		      goto success;
#endif
	      goto failure;
	  }
	  case D_EQ2 >> 24: {
	      uint32_t data1 = *dp;
	      uint32_t data2 = dp[1];
#if CISCY_UNROLL == 2
	      int i = 0;
	      switch (n % 4) {
		  do {
		    case 3:
		      if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		      i++;
		    case 2:
		      if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		      i++;
		    case 1:
		      if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		      i++;
		    case 0:
		      if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		      i++;
		  } while (i <= n);
	      }
#elif CISCY_UNROLL
	      int i = 0;
	      switch (n) {
		case 7: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 6: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 5: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 4: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 3: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 2: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 1: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
		case 0: if (data1 == in->arg[i] || data2 == in->arg[i]) goto success;
		  i++;
	      }
#else
	      for (int i = 0; i <= n; i++)
		  if (data1 == in->arg[i] || data2 == in->arg[i])
		      goto success;
#endif
	      goto failure;
	  }
	  case D_MEQ >> 24: {
	      uint32_t data = *dp;
#if CISCY_UNROLL
	      int i = 0;
	      switch (n) {
		case 7: if ((data & in->arg[i+1]) == in->arg[i]) goto success;
			    i += 2;
		case 5: if ((data & in->arg[i+1]) == in->arg[i]) goto success;
			    i += 2;
		case 3: if ((data & in->arg[i+1]) == in->arg[i]) goto success;
			    i += 2;
		case 1: if ((data & in->arg[i+1]) == in->arg[i]) goto success;
			    i += 2;
	      }
#else
	      for (int i = 0; i <= n; i += 2)
		  if ((data & in->arg[i + 1]) == in->arg[i])
		      goto success;
#endif
	      goto failure;
	  }
	  case D_LEQ >> 24:
	    for (int i = 0; i <= n; i++)
		if (dp[i] != in->arg[i])
		    goto failure;
	    goto success;
	  case L1_SEQ >> 24: {
	      uint16_t data = * (const uint16_t *) dp;
#if CISCY_UNROLL == 2
	      int i = 0;
	      switch (n % 4) {
		  do {
		    case 3:
		      if (data == in->sarg[i++]) goto success;
		    case 2:
		      if (data == in->sarg[i++]) goto success;
		    case 1:
		      if (data == in->sarg[i++]) goto success;
		    case 0:
		      if (data == in->sarg[i++]) goto success;
		  } while (i <= n);
	      }
#elif CISCY_UNROLL
	      int i = 0;
	      switch (n) {
		case 13: if (data == in->sarg[i++]) goto success;
		case 12: if (data == in->sarg[i++]) goto success;
		case 11: if (data == in->sarg[i++]) goto success;
		case 10: if (data == in->sarg[i++]) goto success;
		case 9: if (data == in->sarg[i++]) goto success;
		case 8: if (data == in->sarg[i++]) goto success;
		case 7: if (data == in->sarg[i++]) goto success;
		case 6: if (data == in->sarg[i++]) goto success;
		case 5: if (data == in->sarg[i++]) goto success;
		case 4: if (data == in->sarg[i++]) goto success;
		case 3: if (data == in->sarg[i++]) goto success;
		case 2: if (data == in->sarg[i++]) goto success;
		case 1: if (data == in->sarg[i++]) goto success;
		case 0: if (data == in->sarg[i++]) goto success;
	      }
#else
	      for (int i = 0; i <= n; i++)
		  if (data == in->sarg[i])
		      goto success;
#endif
	      goto failure;
	  }
	  case L1_SEQ2 >> 24: {
	      uint16_t data1 = * (const uint16_t *) dp;
	      uint16_t data2 = ((const uint16_t *) dp)[1];
#if CISCY_UNROLL == 2
	      int i = 0;
	      switch (n % 4) {
		  do {
		    case 3:
		      if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		      i++;
		    case 2:
		      if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		      i++;
		    case 1:
		      if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		      i++;
		    case 0:
		      if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		      i++;
		  } while (i <= n);
	      }
#elif CISCY_UNROLL
	      int i = 0;
	      switch (n) {
		case 13: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 12: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 11: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 10: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 9: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 8: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 7: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 6: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 5: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 4: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 3: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 2: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 1: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
		case 0: if (data1 == in->sarg[i] || data2 == in->sarg[i]) goto success;
		  i++;
	      }
#else
	      for (int i = 0; i <= n; i++)
		  if (data1 == in->sarg[i] || data2 == in->sarg[i])
		      goto success;
#endif
	      goto failure;
	  }
	  case L1_SRNG >> 24: {
	      uint16_t data = * (const uint16_t *) dp;
	      for (int i = 0; i <= n; i += 2)
		  if (data >= in->sarg[i] && data < in->sarg[i + 1])
		      goto success;
	      goto failure;
	  }
	  case L1_LEQ >> 24:
	    for (int i = 0; i <= n; i++)
		if (dp[i] != in->arg[i])
		    goto failure;
	    goto success;
	}
      failure:
	if (!(in->cmd & NEG))
	    goto total_failure;
	continue;
      success:
	if (in->cmd & NEG)
	    goto total_failure;
    }

    output(0).push(p);
    return;

  total_failure:
    p->kill();
}

#include <click/vector.cc>
CLICK_ENDDECLS
EXPORT_ELEMENT(CiscyFilter)
